package com.eastfair.auth.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.eastfair.auth.dao.RoleMapper;
import com.eastfair.auth.dto.RoleDTO;
import com.eastfair.auth.entity.MenuRole;
import com.eastfair.auth.entity.Role;
import com.eastfair.auth.service.*;
import com.eastfair.auth.vo.RoleVo;
import com.eastfair.boot.service.SuperCacheServiceImpl;
import com.eastfair.cache.auth.RoleCacheKeyBuilder;
import com.eastfair.cache.repository.CacheOps;
import com.eastfair.constant.BusinessConstant;
import com.eastfair.core.base.R;
import com.eastfair.core.context.ContextUtil;
import com.eastfair.core.model.CacheKeyBuilder;
import com.eastfair.projectcore.api.ProjectCoreServiceFeign;
import com.eastfair.core.snowflake.DefaultUidGenerator;
import com.eastfair.projectcore.dto.MenuDTO;
import com.eastfair.projectcore.entity.ProjectDb;
import com.eastfair.projectcore.vo.MenuVO;
import com.eastfair.util.CodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static com.eastfair.core.base.R.SUCCESS_CODE;

/**
 * <p>
 * 业务实现类
 *
 * </p>
 *
 * @author ligang
 * @date 2021-07-23
 */
@Slf4j
@Service
@DS("#thread.tenant")
public class RoleServiceImpl extends SuperCacheServiceImpl<RoleMapper, Role> implements RoleService {
    @Autowired
    protected CacheOps cacheOps;

    @Autowired
    ProjectCoreServiceFeign projectCoreServiceFeign;

    @Autowired
    MenuRoleService menuRoleService;

    @Autowired
    OrganizationRoleService orgRoleService;

    @Autowired
    FunctionRoleService functionRoleService;

    @Autowired
    UserAccountService userAccountService;

    /**************sharding 分库分表操作 *****************/
    @Override
    public boolean updateByIdOfShard(Role entity) {
        UpdateWrapper<Role> updateWrapper = new UpdateWrapper<>();
        //可将指定字段更新为null
        updateWrapper.lambda().eq(Role::getProjectId, entity.getProjectId()).eq(Role::getId, entity.getId());
        entity.setProjectId(null);
        //删除缓存
        delCache(entity);
        return SqlHelper.retBool(this.getBaseMapper().update(entity, updateWrapper));
    }

    /**
     * 处理逻辑删除相关处理
     *
     * @param modelList 实体
     * @return 是否成功
     */
    @Override
    protected R<Boolean> handlerRemoveByIdsOfLogic(Collection<Role> modelList) {
        modelList.stream().forEach(s -> {
            s.setIsDeleted(BusinessConstant.YES);
        });
        // TODO: 2021/10/29 这里需要操作其他业务，如清空角色等
        return R.successDef();
    }

    /**
     * 处理新增相关处理
     *
     * @param model 实体
     * @return 是否成功
     */
    @Override
    protected R<Boolean> handlerSave(Role model) {
        //雪花ID
        model.setId(DefaultUidGenerator.getUidOfProjectId(model.getProjectId()));
        //code 码
        if (StringUtils.isBlank(model.getCode())) {
            model.setCode(queryCode(model));
        }
        return R.successDef();
    }

    public String queryCode(Role model) {
        String code = nonredundantCode();
        return code;
    }

    /**
     * 获取不重复的code
     *
     * @return
     */
    public String nonredundantCode() {
        String code = CodeUtils.randomCode(6);
        //是否重复
        int num = queryCodeSize(code);
        if (num > 0) {
            nonredundantCode();
        }
        return code;
    }

    public int queryCodeSize(String code) {
        QueryWrapper<Role> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(code)) {
            queryWrapper.lambda().eq(Role::getCode, code);
        }
        queryWrapper.lambda().eq(Role::getIsDeleted, BusinessConstant.DELETE_NO);
        return count(queryWrapper);
    }


    @Override
    protected CacheKeyBuilder cacheKeyBuilder() {
        return new RoleCacheKeyBuilder();
    }

    @Override
    public R<Role> createRole(RoleDTO roleDTO) {
        Role role = new Role();
        BeanUtils.copyProperties(roleDTO, role);
        save(role);
        //赋权角色菜单关系
        //项目为管理员项目，则不用校验
        if (!verifyProjectIsAdmin(roleDTO.getProjectId())) {
            //校验菜单是否超出该操作员所拥有的菜单
            if (ContextUtil.getUserId() != null && ContextUtil.getUserId() != 0) {
                R r = userAccountService.verifyMenu(ContextUtil.getUserId(), roleDTO.getMenuIds());
                if (SUCCESS_CODE != r.getCode()) {
                    return r;
                }
            }
        }
        roleDTO.setId(role.getId());
        reloadRoleRelationOfMenu(roleDTO);
        return R.success(role);
    }

    /**
     * 校验项目是否是管理员项目
     *
     * @param projectId
     * @return
     */
    public boolean verifyProjectIsAdmin(Long projectId) {
        //项目为管理员项目，则不用校验
        R<ProjectDb> r = projectCoreServiceFeign.getProjectById(projectId);
        if (r != null && SUCCESS_CODE == r.getCode() && r.getData() != null && r.getData().getIsAdmin() != null && r.getData().getIsAdmin() == BusinessConstant.YES) {
            return true;
        }
        return false;
    }

    @Override
    public R<Role> updateRole(RoleDTO roleDTO) {
        Role role = getById(roleDTO.getId());
        if (StringUtils.isNotBlank(roleDTO.getName())) {
            role.setName(roleDTO.getName());
        }
        if (StringUtils.isNotBlank(roleDTO.getDescription())) {
            role.setDescription(roleDTO.getDescription());
        }
        updateByIdOfShard(role);
        //赋权角色菜单关系
        if (!verifyProjectIsAdmin(roleDTO.getProjectId())) {
            //校验菜单是否超出该操作员所拥有的菜单
            if (ContextUtil.getUserId() != null) {
                R r = userAccountService.verifyMenu(ContextUtil.getUserId(), roleDTO.getMenuIds());
                if (SUCCESS_CODE != r.getCode()) {
                    return r;
                }
            }
        }
        roleDTO.setId(role.getId());
        reloadRoleRelationOfMenu(roleDTO);

        return R.success(role);
    }

    @Override
    public R delRole(List<Long> roleIds) {
        //删除角色，并删除角色下的菜单关系和组织关系
        if (roleIds == null || roleIds.isEmpty()) {
            return R.success();
        }
        //剔除角色关系
        removeRoleRelation(roleIds);
        //删除角色
        for (Long roleId : roleIds) {
            Role role = getById(roleId);
            if (role != null) {
                role.setIsDeleted(BusinessConstant.DELETE_YES);
                updateByIdOfShard(role);
            }
        }
        return R.success();
    }

    @Override
    public R removeRoleRelation(List<Long> roleIds) {
        //删除组织关系
        return orgRoleService.removeOfRole(roleIds);
    }

    @Override
    public R removeRoleRelationOfMenu(List<Long> roleIds) {
        //删除菜单关系
        return menuRoleService.removeRole(roleIds);
    }

    @Override
    public R reloadRoleRelationOfMenu(RoleDTO roleDTO) {
        //剔除关系
        removeRoleRelationOfMenu(Arrays.asList(roleDTO.getId()));
        //重新添加关系
        menuRoleService.addOfMenu(roleDTO.getId(), roleDTO.getMenuIds());
        return R.success();
    }

    @Override
    public List<RoleVo> findAllById(List<Long> roleIds) {
        //查询
        QueryWrapper<Role> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().in(Role::getId, roleIds);
        queryWrapper.lambda().eq(Role::getIsDeleted, BusinessConstant.DELETE_NO);
        List<Role> roleList = list(queryWrapper);
        return doToVos(roleList);
    }

    @Override
    public List<RoleVo> findAllMenuById(List<Long> roleIds) {
        //角色信息
        List<RoleVo> roleVoList = new ArrayList<>();
        //角色和菜单的关系表
        R<List<MenuRole>> r = menuRoleService.findOfRole(roleIds);
        if (SUCCESS_CODE == r.getCode()) {
            List<MenuRole> menuRoleList = r.getData();
            //根据角色分组
            Map<Long, List<MenuRole>> roleGroupList = menuRoleList.stream().collect(Collectors.groupingBy(MenuRole::getRoleId));
            for (Long roleId : roleGroupList.keySet()) {
                List<MenuRole> menuRoleList1 = roleGroupList.get(roleId);
                if (menuRoleList1 != null && !menuRoleList1.isEmpty()) {
                    //查询角色下的菜单
                    MenuDTO menuDTO = new MenuDTO();
                    menuDTO.setMenuIds(menuRoleList1.stream().map(MenuRole::getMenuId).collect(Collectors.toList()));
                    R<List<MenuVO>> menuVoList = projectCoreServiceFeign.findAllByChildId(menuDTO);
                    if (SUCCESS_CODE == menuVoList.getCode()) {
                        RoleVo roleVo = new RoleVo();
                        Role role = getById(roleId);
                        BeanUtils.copyProperties(role,roleVo);
                        roleVo.setMenuVOList(menuVoList.getData());
                        roleVo.setMenuIds(menuRoleList1.stream().map(MenuRole::getMenuId).collect(Collectors.toList()));
                        roleVoList.add(roleVo);
                    }
                }
            }
        }
        return roleVoList;
    }

    @Override
    public List<Long> findMenuIdById(Long roleId) {
        //角色信息
        List<RoleVo> roleVoList = new ArrayList<>();
        //角色和菜单的关系表
        R<List<MenuRole>> r = menuRoleService.findOfRole(Arrays.asList(roleId));
        if (SUCCESS_CODE == r.getCode()) {
            List<MenuRole> menuRoleList = r.getData();
            return menuRoleList.stream().map(MenuRole::getMenuId).collect(Collectors.toList());
        }
        return null;
    }

    @Override
    public R removeRoleRelationOfFunction(List<Long> roleIds) {
        //删除事件关系
        return functionRoleService.removeRole(roleIds);
    }

    @Override
    public R reloadRoleRelationOfFunction(RoleDTO roleDTO) {
        //剔除关系
        removeRoleRelationOfFunction(Arrays.asList(roleDTO.getId()));
        //重新添加关系
        functionRoleService.addOfFunction(roleDTO.getId(), roleDTO.getFunctionIds());
        return R.success();
    }

    @Override
    public RoleVo getAllById(Long roleId) {
        Role role = getById(roleId);
        if (role == null) {
            return null;
        }
        RoleVo roleVo = doToVo(role);
        //查询菜单集合
        roleVo.setMenuIds(findMenuIdById(roleId));
        return roleVo;
    }

    @Override
    public List<RoleVo> queryAllRole() {
        //查询
        QueryWrapper<Role> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().in(Role::getProjectId, ContextUtil.getProjectId());
        queryWrapper.lambda().eq(Role::getIsDeleted, BusinessConstant.DELETE_NO);
        queryWrapper.lambda().eq(Role::getIsEnabled, BusinessConstant.ENABLE_YES);
        List<Role> roleList = list(queryWrapper);
        return doToVos(roleList);
    }

    @Override
    public List<RoleVo> copyRole(List<RoleVo> roleVoList) {
        List<RoleVo> result = new ArrayList<>();
        if(roleVoList!=null && !roleVoList.isEmpty()){
            for(RoleVo roleVo : roleVoList){
                Role role = new Role();
                role.setName(roleVo.getName());
                role.setProjectId(ContextUtil.getProjectId());
                save(role);
                //赋菜单
                RoleDTO roleDTO = new RoleDTO();
                roleDTO.setId(role.getId());
                roleDTO.setMenuIds(roleVo.getMenuIds());
                reloadRoleRelationOfMenu(roleDTO);
                RoleVo roleVo1 = new RoleVo();
                BeanUtils.copyProperties(role,roleVo1);
                result.add(roleVo1);
            }
        }
        return result;
    }

    public RoleVo doToVo(Role role) {
        RoleVo roleVo = new RoleVo();
        BeanUtils.copyProperties(role, roleVo);
        roleVo.setId(role.getId());
        return roleVo;
    }

    public List<RoleVo> doToVos(List<Role> roleList) {
        if (roleList == null || roleList.isEmpty()) {
            return null;
        }
        List<RoleVo> result = new ArrayList<>();
        for (Role role : roleList) {
            result.add(doToVo(role));
        }
        return result;
    }
}
